// utils/clientSelectorGenerator.ts

interface Coordinates {
  x: number;
  y: number;
}

interface ElementInfo {
  tagName: string;
  hasOnlyText?: boolean;
  innerText?: string;
  url?: string;
  imageUrl?: string;
  attributes?: Record<string, string>;
  innerHTML?: string;
  outerHTML?: string;
  isIframeContent?: boolean;
  isFrameContent?: boolean;
  iframeURL?: string;
  frameURL?: string;
  iframeIndex?: number;
  frameIndex?: number;
  frameHierarchy?: string[];
  isShadowRoot?: boolean;
  shadowRootMode?: string;
  shadowRootContent?: string;
}

interface SelectorResult {
  generalSelector: string;
}

interface Selectors {
  id?: string | null;
  generalSelector?: string | null;
  attrSelector?: string | null;
  testIdSelector?: string | null;
  text?: string;
  href?: string;
  hrefSelector?: string | null;
  accessibilitySelector?: string | null;
  formSelector?: string | null;
  relSelector?: string | null;
  iframeSelector?: {
    full: string;
    isIframe: boolean;
  } | null;
  shadowSelector?: {
    full: string;
    mode: string;
  } | null;
}

export enum ActionType {
  AwaitText = "awaitText",
  Click = "click",
  DragAndDrop = "dragAndDrop",
  Screenshot = "screenshot",
  Hover = "hover",
  Input = "input",
  Keydown = "keydown",
  Load = "load",
  Navigate = "navigate",
  Scroll = "scroll",
}

enum TagName {
  A = "A",
  B = "B",
  Cite = "CITE",
  EM = "EM",
  Input = "INPUT",
  Select = "SELECT",
  Span = "SPAN",
  Strong = "STRONG",
  TextArea = "TEXTAREA",
}

interface Action {
  type: ActionType;
  tagName: TagName;
  inputType?: string;
  value?: string;
  selectors: Selectors;
  timestamp: number;
  isPassword: boolean;
  hasOnlyText: boolean;
}

class ClientSelectorGenerator {
  private listSelector: string = "";
  private getList: boolean = false;
  private paginationMode: boolean = false;

  // Add setter methods for state management
  public setListSelector(selector: string): void {
    this.listSelector = selector;
  }

  public setGetList(getList: boolean): void {
    this.getList = getList;
  }

  public setPaginationMode(paginationMode: boolean): void {
    this.paginationMode = paginationMode;
  }

  public getCurrentState(): {
    listSelector: string;
    getList: boolean;
    paginationMode: boolean;
  } {
    return {
      listSelector: this.listSelector,
      getList: this.getList,
      paginationMode: this.paginationMode,
    };
  }

  public getElementInformation = (
    iframeDoc: Document,
    coordinates: Coordinates,
    listSelector: string,
    getList: boolean
  ) => {
    try {
      if (!getList || listSelector !== "") {
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findDeepestElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            let deepestElement = elements[0];
            let maxDepth = 0;

            for (const element of elements) {
              let depth = 0;
              let current = element;

              while (current) {
                depth++;
                if (current.parentElement) {
                  current = current.parentElement;
                } else {
                  break;
                }
              }

              if (depth > maxDepth) {
                maxDepth = depth;
                deepestElement = element;
              }
            }

            return deepestElement;
          };

          let deepestElement = findDeepestElement(elements);
          if (!deepestElement) return null;

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          const isInFrameset = () => {
            let node = deepestElement;
            while (node && node.parentElement) {
              if (node.tagName === "FRAMESET" || node.tagName === "FRAME") {
                return true;
              }
              node = node.parentElement;
            }
            return false;
          };

          if (deepestElement.tagName === "IFRAME") {
            let currentIframe = deepestElement as HTMLIFrameElement;
            let depth = 0;
            const MAX_IFRAME_DEPTH = 4;

            while (currentIframe && depth < MAX_IFRAME_DEPTH) {
              try {
                const iframeRect = currentIframe.getBoundingClientRect();
                const iframeX = x - iframeRect.left;
                const iframeY = y - iframeRect.top;

                const iframeDocument =
                  currentIframe.contentDocument ||
                  currentIframe.contentWindow?.document;
                if (!iframeDocument) break;

                const iframeElement = iframeDocument.elementFromPoint(
                  iframeX,
                  iframeY
                ) as HTMLElement;
                if (!iframeElement) break;

                deepestElement = traverseShadowDOM(iframeElement);

                if (iframeElement.tagName === "IFRAME") {
                  currentIframe = iframeElement as HTMLIFrameElement;
                  depth++;
                } else {
                  break;
                }
              } catch (error) {
                console.warn("Cannot access iframe content:", error);
                break;
              }
            }
          } else if (deepestElement.tagName === "FRAME" || isInFrameset()) {
            const framesToCheck = [];

            if (deepestElement.tagName === "FRAME") {
              framesToCheck.push(deepestElement as HTMLFrameElement);
            }

            if (isInFrameset()) {
              iframeDoc.querySelectorAll("frame").forEach((frame) => {
                framesToCheck.push(frame as HTMLFrameElement);
              });
            }

            let frameDepth = 0;
            const MAX_FRAME_DEPTH = 4;

            const processFrames = (
              frames: HTMLFrameElement[],
              currentDepth: number
            ) => {
              if (currentDepth >= MAX_FRAME_DEPTH) return;

              for (const frameElement of frames) {
                try {
                  const frameRect = frameElement.getBoundingClientRect();
                  const frameX = x - frameRect.left;
                  const frameY = y - frameRect.top;

                  if (
                    frameX < 0 ||
                    frameY < 0 ||
                    frameX > frameRect.width ||
                    frameY > frameRect.height
                  ) {
                    continue;
                  }

                  const frameDocument =
                    frameElement.contentDocument ||
                    frameElement.contentWindow?.document;

                  if (!frameDocument) continue;

                  const frameElementAtPoint = frameDocument.elementFromPoint(
                    frameX,
                    frameY
                  ) as HTMLElement;
                  if (!frameElementAtPoint) continue;

                  deepestElement = traverseShadowDOM(frameElementAtPoint);

                  if (frameElementAtPoint.tagName === "FRAME") {
                    processFrames(
                      [frameElementAtPoint as HTMLFrameElement],
                      currentDepth + 1
                    );
                  }

                  break;
                } catch (error) {
                  console.warn("Cannot access frame content:", error);
                  continue;
                }
              }
            };

            processFrames(framesToCheck, frameDepth);
          } else {
            deepestElement = traverseShadowDOM(deepestElement);
          }

          return deepestElement;
        };

        const el = getDeepestElementFromPoint(coordinates.x, coordinates.y);

        if (el) {
          // Prioritize Link (DO NOT REMOVE)
          const { parentElement } = el;
          const targetElement =
            parentElement?.tagName === "A" ? parentElement : el;

          const ownerDocument = targetElement.ownerDocument;
          const frameElement = ownerDocument?.defaultView
            ?.frameElement as HTMLIFrameElement;
          const isIframeContent = Boolean(frameElement);
          const isFrameContent = frameElement?.tagName === "FRAME";

          const containingShadowRoot =
            targetElement.getRootNode() as ShadowRoot;
          const isShadowRoot = containingShadowRoot instanceof ShadowRoot;

          let info: {
            tagName: string;
            hasOnlyText?: boolean;
            innerText?: string;
            url?: string;
            imageUrl?: string;
            attributes?: Record<string, string>;
            innerHTML?: string;
            outerHTML?: string;
            isIframeContent?: boolean;
            isFrameContent?: boolean;
            iframeURL?: string;
            frameURL?: string;
            iframeIndex?: number;
            frameIndex?: number;
            frameHierarchy?: string[];
            isShadowRoot?: boolean;
            shadowRootMode?: string;
            shadowRootContent?: string;
          } = {
            tagName: targetElement?.tagName ?? "",
            isIframeContent,
            isFrameContent,
            isShadowRoot,
          };

          if (isIframeContent || isFrameContent) {
            if (isIframeContent) {
              info.iframeURL = (frameElement as HTMLIFrameElement).src;
            } else {
              info.frameURL = frameElement.src;
            }

            let currentFrame = frameElement;
            const frameHierarchy: string[] = [];
            let frameIndex = 0;

            while (currentFrame) {
              frameHierarchy.unshift(
                currentFrame.id ||
                  currentFrame.getAttribute("name") ||
                  currentFrame.src ||
                  `${currentFrame.tagName.toLowerCase()}[${frameIndex}]`
              );

              const parentDoc = currentFrame.ownerDocument;
              currentFrame = parentDoc?.defaultView
                ?.frameElement as HTMLIFrameElement;
              frameIndex++;
            }

            info.frameHierarchy = frameHierarchy;
            if (isIframeContent) {
              info.iframeIndex = frameIndex - 1;
            } else {
              info.frameIndex = frameIndex - 1;
            }
          }

          if (isShadowRoot) {
            info.shadowRootMode = containingShadowRoot.mode;
            info.shadowRootContent = containingShadowRoot.innerHTML;
          }

          if (targetElement) {
            info.attributes = Array.from(targetElement.attributes).reduce(
              (acc, attr) => {
                acc[attr.name] = attr.value;
                return acc;
              },
              {} as Record<string, string>
            );

            if (targetElement.tagName === "A") {
              info.url = (targetElement as HTMLAnchorElement).href;
              info.innerText = targetElement.textContent ?? "";
            } else if (targetElement.tagName === "IMG") {
              info.imageUrl = (targetElement as HTMLImageElement).src;
            } else if (targetElement?.tagName === "SELECT") {
              const selectElement = targetElement as HTMLSelectElement;
              info.innerText =
                selectElement.options[selectElement.selectedIndex]?.text ?? "";
              info.attributes = {
                ...info.attributes,
                selectedValue: selectElement.value,
              };
            } else if (
              (targetElement?.tagName === "INPUT" &&
                (targetElement as HTMLInputElement).type === "time") ||
              (targetElement as HTMLInputElement).type === "date"
            ) {
              info.innerText = (targetElement as HTMLInputElement).value;
            } else {
              info.hasOnlyText =
                targetElement.children.length === 0 &&
                targetElement.textContent !== null &&
                targetElement.textContent.trim().length > 0;
              info.innerText = targetElement.textContent ?? "";
            }

            info.innerHTML = targetElement.innerHTML;
            info.outerHTML = targetElement.outerHTML;
          }

          return info;
        }
        return null;
      } else {
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findContainerElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            for (let i = 0; i < elements.length; i++) {
              const element = elements[i];
              const rect = element.getBoundingClientRect();

              if (rect.width >= 30 && rect.height >= 30) {
                const hasChildrenInList = elements.some(
                  (otherElement, j) => i !== j && element.contains(otherElement)
                );

                if (hasChildrenInList) {
                  return element;
                }
              }
            }

            return elements[0];
          };

          let deepestElement = findContainerElement(elements);
          if (!deepestElement) return null;

          if (deepestElement.tagName === "A") {
            for (let i = 1; i < elements.length; i++) {
              const sibling = elements[i];
              if (
                !deepestElement.contains(sibling) &&
                !sibling.contains(deepestElement)
              ) {
                const anchorRect = deepestElement.getBoundingClientRect();
                const siblingRect = sibling.getBoundingClientRect();

                const isOverlapping = !(
                  siblingRect.right < anchorRect.left ||
                  siblingRect.left > anchorRect.right ||
                  siblingRect.bottom < anchorRect.top ||
                  siblingRect.top > anchorRect.bottom
                );

                if (isOverlapping) {
                  deepestElement = sibling;
                  break;
                }
              }
            }
          }

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          const isInFrameset = () => {
            let node = deepestElement;
            while (node && node.parentElement) {
              if (node.tagName === "FRAMESET" || node.tagName === "FRAME") {
                return true;
              }
              node = node.parentElement;
            }
            return false;
          };

          if (deepestElement.tagName === "IFRAME") {
            let currentIframe = deepestElement as HTMLIFrameElement;
            let depth = 0;
            const MAX_IFRAME_DEPTH = 4;

            while (currentIframe && depth < MAX_IFRAME_DEPTH) {
              try {
                const iframeRect = currentIframe.getBoundingClientRect();
                const iframeX = x - iframeRect.left;
                const iframeY = y - iframeRect.top;

                const iframeDocument =
                  currentIframe.contentDocument ||
                  currentIframe.contentWindow?.document;
                if (!iframeDocument) break;

                const iframeElement = iframeDocument.elementFromPoint(
                  iframeX,
                  iframeY
                ) as HTMLElement;
                if (!iframeElement) break;

                deepestElement = traverseShadowDOM(iframeElement);

                if (iframeElement.tagName === "IFRAME") {
                  currentIframe = iframeElement as HTMLIFrameElement;
                  depth++;
                } else {
                  break;
                }
              } catch (error) {
                console.warn("Cannot access iframe content:", error);
                break;
              }
            }
          } else if (deepestElement.tagName === "FRAME" || isInFrameset()) {
            const framesToCheck = [];

            if (deepestElement.tagName === "FRAME") {
              framesToCheck.push(deepestElement as HTMLFrameElement);
            }

            if (isInFrameset()) {
              iframeDoc.querySelectorAll("frame").forEach((frame) => {
                framesToCheck.push(frame as HTMLFrameElement);
              });
            }

            let frameDepth = 0;
            const MAX_FRAME_DEPTH = 4;

            const processFrames = (
              frames: HTMLFrameElement[],
              currentDepth: number
            ) => {
              if (currentDepth >= MAX_FRAME_DEPTH) return;

              for (const frameElement of frames) {
                try {
                  const frameRect = frameElement.getBoundingClientRect();
                  const frameX = x - frameRect.left;
                  const frameY = y - frameRect.top;

                  if (
                    frameX < 0 ||
                    frameY < 0 ||
                    frameX > frameRect.width ||
                    frameY > frameRect.height
                  ) {
                    continue;
                  }

                  const frameDocument =
                    frameElement.contentDocument ||
                    frameElement.contentWindow?.document;

                  if (!frameDocument) continue;

                  const frameElementAtPoint = frameDocument.elementFromPoint(
                    frameX,
                    frameY
                  ) as HTMLElement;
                  if (!frameElementAtPoint) continue;

                  deepestElement = traverseShadowDOM(frameElementAtPoint);

                  if (frameElementAtPoint.tagName === "FRAME") {
                    processFrames(
                      [frameElementAtPoint as HTMLFrameElement],
                      currentDepth + 1
                    );
                  }

                  break;
                } catch (error) {
                  console.warn("Cannot access frame content:", error);
                  continue;
                }
              }
            };

            processFrames(framesToCheck, frameDepth);
          } else {
            deepestElement = traverseShadowDOM(deepestElement);
          }

          return deepestElement;
        };

        const originalEl = getDeepestElementFromPoint(
          coordinates.x,
          coordinates.y
        );
        if (originalEl) {
          let element = originalEl;

          if (element.tagName === "TD" || element.tagName === "TH") {
            const tableParent = element.closest("table");
            if (tableParent) {
              element = tableParent;
            }
          }

          const ownerDocument = element.ownerDocument;
          const frameElement = ownerDocument?.defaultView?.frameElement;
          const isIframeContent = Boolean(frameElement);
          const isFrameContent = frameElement?.tagName === "FRAME";

          const containingShadowRoot = element.getRootNode() as ShadowRoot;
          const isShadowRoot = containingShadowRoot instanceof ShadowRoot;

          let info: {
            tagName: string;
            hasOnlyText?: boolean;
            innerText?: string;
            url?: string;
            imageUrl?: string;
            attributes?: Record<string, string>;
            innerHTML?: string;
            outerHTML?: string;
            isIframeContent?: boolean;
            isFrameContent?: boolean;
            iframeURL?: string;
            frameURL?: string;
            iframeIndex?: number;
            frameIndex?: number;
            frameHierarchy?: string[];
            isShadowRoot?: boolean;
            shadowRootMode?: string;
            shadowRootContent?: string;
          } = {
            tagName: element?.tagName ?? "",
            isIframeContent,
            isFrameContent,
            isShadowRoot,
          };

          if (isIframeContent || isFrameContent) {
            if (isIframeContent && !isFrameContent) {
              info.iframeURL = (frameElement as HTMLIFrameElement).src;
            } else if (isFrameContent) {
              info.frameURL = (frameElement as HTMLFrameElement).src;
            }

            let currentFrame = frameElement;
            const frameHierarchy: string[] = [];
            let frameIndex = 0;

            while (currentFrame) {
              frameHierarchy.unshift(
                currentFrame.id ||
                  currentFrame.getAttribute("name") ||
                  (currentFrame as HTMLFrameElement).src ||
                  `${currentFrame.tagName.toLowerCase()}[${frameIndex}]`
              );

              const parentDoc = currentFrame.ownerDocument;
              currentFrame = parentDoc?.defaultView?.frameElement;
              frameIndex++;
            }

            info.frameHierarchy = frameHierarchy;
            if (isIframeContent && !isFrameContent) {
              info.iframeIndex = frameIndex - 1;
            } else if (isFrameContent) {
              info.frameIndex = frameIndex - 1;
            }
          }

          if (isShadowRoot) {
            info.shadowRootMode = containingShadowRoot.mode;
            info.shadowRootContent = containingShadowRoot.innerHTML;
          }

          if (element) {
            info.attributes = Array.from(element.attributes).reduce(
              (acc, attr) => {
                acc[attr.name] = attr.value;
                return acc;
              },
              {} as Record<string, string>
            );

            if (element.tagName === "A") {
              info.url = (element as HTMLAnchorElement).href;
              info.innerText = element.textContent ?? "";
            } else if (element.tagName === "IMG") {
              info.imageUrl = (element as HTMLImageElement).src;
            } else if (element?.tagName === "SELECT") {
              const selectElement = element as HTMLSelectElement;
              info.innerText =
                selectElement.options[selectElement.selectedIndex]?.text ?? "";
              info.attributes = {
                ...info.attributes,
                selectedValue: selectElement.value,
              };
            } else if (
              element?.tagName === "INPUT" &&
              ((element as HTMLInputElement).type === "time" ||
                (element as HTMLInputElement).type === "date")
            ) {
              info.innerText = (element as HTMLInputElement).value;
            } else {
              info.hasOnlyText =
                element.children.length === 0 &&
                element.textContent !== null &&
                element.textContent.trim().length > 0;
              info.innerText = element.textContent ?? "";
            }

            info.innerHTML = element.innerHTML;
            info.outerHTML = element.outerHTML;
          }

          return info;
        }
        return null;
      }
    } catch (error) {
      const { message, stack } = error as Error;
      console.error("Error while retrieving selector:", message);
      console.error("Stack:", stack);
    }
  };

  private getRect = (
    iframeDoc: Document,
    coordinates: Coordinates,
    listSelector: string,
    getList: boolean,
    isDOMMode: boolean = false
  ) => {
    try {
      if (!getList || listSelector !== "") {
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findDeepestElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            let deepestElement = elements[0];
            let maxDepth = 0;

            for (const element of elements) {
              let depth = 0;
              let current = element;

              while (current) {
                depth++;
                if (current.parentElement) {
                  current = current.parentElement;
                } else {
                  break;
                }
              }

              if (depth > maxDepth) {
                maxDepth = depth;
                deepestElement = element;
              }
            }

            return deepestElement;
          };

          let deepestElement = findDeepestElement(elements);
          if (!deepestElement) return null;

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          const isInFrameset = () => {
            let node = deepestElement;
            while (node && node.parentElement) {
              if (node.tagName === "FRAMESET" || node.tagName === "FRAME") {
                return true;
              }
              node = node.parentElement;
            }
            return false;
          };

          if (deepestElement.tagName === "IFRAME") {
            let currentIframe = deepestElement as HTMLIFrameElement;
            let depth = 0;
            const MAX_IFRAME_DEPTH = 4;

            while (currentIframe && depth < MAX_IFRAME_DEPTH) {
              try {
                const iframeRect = currentIframe.getBoundingClientRect();
                const iframeX = x - iframeRect.left;
                const iframeY = y - iframeRect.top;

                const iframeDocument =
                  currentIframe.contentDocument ||
                  currentIframe.contentWindow?.document;
                if (!iframeDocument) break;

                const iframeElement = iframeDocument.elementFromPoint(
                  iframeX,
                  iframeY
                ) as HTMLElement;
                if (!iframeElement) break;

                deepestElement = traverseShadowDOM(iframeElement);

                if (iframeElement.tagName === "IFRAME") {
                  currentIframe = iframeElement as HTMLIFrameElement;
                  depth++;
                } else {
                  break;
                }
              } catch (error) {
                console.warn("Cannot access iframe content:", error);
                break;
              }
            }
          } else if (deepestElement.tagName === "FRAME" || isInFrameset()) {
            const framesToCheck = [];

            if (deepestElement.tagName === "FRAME") {
              framesToCheck.push(deepestElement as HTMLFrameElement);
            }

            if (isInFrameset()) {
              iframeDoc.querySelectorAll("frame").forEach((frame) => {
                framesToCheck.push(frame as HTMLFrameElement);
              });
            }

            let frameDepth = 0;
            const MAX_FRAME_DEPTH = 4;

            const processFrames = (
              frames: HTMLFrameElement[],
              currentDepth: number
            ) => {
              if (currentDepth >= MAX_FRAME_DEPTH) return;

              for (const frameElement of frames) {
                try {
                  const frameRect = frameElement.getBoundingClientRect();
                  const frameX = x - frameRect.left;
                  const frameY = y - frameRect.top;

                  if (
                    frameX < 0 ||
                    frameY < 0 ||
                    frameX > frameRect.width ||
                    frameY > frameRect.height
                  ) {
                    continue;
                  }

                  const frameDocument =
                    frameElement.contentDocument ||
                    frameElement.contentWindow?.document;

                  if (!frameDocument) continue;

                  const frameElementAtPoint = frameDocument.elementFromPoint(
                    frameX,
                    frameY
                  ) as HTMLElement;
                  if (!frameElementAtPoint) continue;

                  deepestElement = traverseShadowDOM(frameElementAtPoint);

                  if (frameElementAtPoint.tagName === "FRAME") {
                    processFrames(
                      [frameElementAtPoint as HTMLFrameElement],
                      currentDepth + 1
                    );
                  }

                  break;
                } catch (error) {
                  console.warn("Cannot access frame content:", error);
                  continue;
                }
              }
            };

            processFrames(framesToCheck, frameDepth);
          } else {
            deepestElement = traverseShadowDOM(deepestElement);
          }

          return deepestElement;
        };

        const el = getDeepestElementFromPoint(coordinates.x, coordinates.y);
        if (el) {
          // Prioritize Link (DO NOT REMOVE)
          const { parentElement } = el;
          const element = parentElement?.tagName === "A" ? parentElement : el;

          const rectangle = element?.getBoundingClientRect();
          if (rectangle) {
            const createRectObject = (rect: DOMRect) => ({
              x: rect.x,
              y: rect.y,
              width: rect.width,
              height: rect.height,
              top: rect.top,
              right: rect.right,
              bottom: rect.bottom,
              left: rect.left,
              toJSON() {
                return {
                  x: this.x,
                  y: this.y,
                  width: this.width,
                  height: this.height,
                  top: this.top,
                  right: this.right,
                  bottom: this.bottom,
                  left: this.left,
                };
              },
            });

            if (isDOMMode) {
              // For DOM mode, return iframe-relative coordinates
              return createRectObject(rectangle);
            } else {
              // For screenshot mode, adjust coordinates relative to the top window
              let adjustedRect = createRectObject(rectangle);
              let currentWindow = element.ownerDocument.defaultView;

              while (currentWindow !== window.top) {
                const frameElement =
                  currentWindow?.frameElement as HTMLIFrameElement;
                if (!frameElement) break;

                const frameRect = frameElement.getBoundingClientRect();
                adjustedRect = createRectObject({
                  x: adjustedRect.x + frameRect.x,
                  y: adjustedRect.y + frameRect.y,
                  width: adjustedRect.width,
                  height: adjustedRect.height,
                  top: adjustedRect.top + frameRect.top,
                  right: adjustedRect.right + frameRect.left,
                  bottom: adjustedRect.bottom + frameRect.top,
                  left: adjustedRect.left + frameRect.left,
                } as DOMRect);

                currentWindow = frameElement.ownerDocument.defaultView;
              }

              return adjustedRect;
            }
          }
        }
        return null;
      } else {
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findContainerElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            for (let i = 0; i < elements.length; i++) {
              const element = elements[i];
              const rect = element.getBoundingClientRect();

              if (rect.width >= 30 && rect.height >= 30) {
                const hasChildrenInList = elements.some(
                  (otherElement, j) => i !== j && element.contains(otherElement)
                );

                if (hasChildrenInList) {
                  return element;
                }
              }
            }

            return elements[0];
          };

          let deepestElement = findContainerElement(elements);
          if (!deepestElement) return null;

          if (deepestElement.tagName === "A") {
            for (let i = 1; i < elements.length; i++) {
              const sibling = elements[i];
              if (
                !deepestElement.contains(sibling) &&
                !sibling.contains(deepestElement)
              ) {
                const anchorRect = deepestElement.getBoundingClientRect();
                const siblingRect = sibling.getBoundingClientRect();

                const isOverlapping = !(
                  siblingRect.right < anchorRect.left ||
                  siblingRect.left > anchorRect.right ||
                  siblingRect.bottom < anchorRect.top ||
                  siblingRect.top > anchorRect.bottom
                );

                if (isOverlapping) {
                  deepestElement = sibling;
                  break;
                }
              }
            }
          }

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          const isInFrameset = () => {
            let node = deepestElement;
            while (node && node.parentElement) {
              if (node.tagName === "FRAMESET" || node.tagName === "FRAME") {
                return true;
              }
              node = node.parentElement;
            }
            return false;
          };

          if (deepestElement.tagName === "IFRAME") {
            let currentIframe = deepestElement as HTMLIFrameElement;
            let depth = 0;
            const MAX_IFRAME_DEPTH = 4;

            while (currentIframe && depth < MAX_IFRAME_DEPTH) {
              try {
                const iframeRect = currentIframe.getBoundingClientRect();
                const iframeX = x - iframeRect.left;
                const iframeY = y - iframeRect.top;

                const iframeDocument =
                  currentIframe.contentDocument ||
                  currentIframe.contentWindow?.document;
                if (!iframeDocument) break;

                const iframeElement = iframeDocument.elementFromPoint(
                  iframeX,
                  iframeY
                ) as HTMLElement;
                if (!iframeElement) break;

                deepestElement = traverseShadowDOM(iframeElement);

                if (iframeElement.tagName === "IFRAME") {
                  currentIframe = iframeElement as HTMLIFrameElement;
                  depth++;
                } else {
                  break;
                }
              } catch (error) {
                console.warn("Cannot access iframe content:", error);
                break;
              }
            }
          } else if (deepestElement.tagName === "FRAME" || isInFrameset()) {
            const framesToCheck = [];

            if (deepestElement.tagName === "FRAME") {
              framesToCheck.push(deepestElement as HTMLFrameElement);
            }

            if (isInFrameset()) {
              iframeDoc.querySelectorAll("frame").forEach((frame) => {
                framesToCheck.push(frame as HTMLFrameElement);
              });
            }

            let frameDepth = 0;
            const MAX_FRAME_DEPTH = 4;

            const processFrames = (
              frames: HTMLFrameElement[],
              currentDepth: number
            ) => {
              if (currentDepth >= MAX_FRAME_DEPTH) return;

              for (const frameElement of frames) {
                try {
                  const frameRect = frameElement.getBoundingClientRect();
                  const frameX = x - frameRect.left;
                  const frameY = y - frameRect.top;

                  if (
                    frameX < 0 ||
                    frameY < 0 ||
                    frameX > frameRect.width ||
                    frameY > frameRect.height
                  ) {
                    continue;
                  }

                  const frameDocument =
                    frameElement.contentDocument ||
                    frameElement.contentWindow?.document;

                  if (!frameDocument) continue;

                  const frameElementAtPoint = frameDocument.elementFromPoint(
                    frameX,
                    frameY
                  ) as HTMLElement;
                  if (!frameElementAtPoint) continue;

                  deepestElement = traverseShadowDOM(frameElementAtPoint);

                  if (frameElementAtPoint.tagName === "FRAME") {
                    processFrames(
                      [frameElementAtPoint as HTMLFrameElement],
                      currentDepth + 1
                    );
                  }

                  break;
                } catch (error) {
                  console.warn("Cannot access frame content:", error);
                  continue;
                }
              }
            };

            processFrames(framesToCheck, frameDepth);
          } else {
            deepestElement = traverseShadowDOM(deepestElement);
          }

          return deepestElement;
        };

        const originalEl = getDeepestElementFromPoint(
          coordinates.x,
          coordinates.y
        );
        if (originalEl) {
          let element = originalEl;

          if (element.tagName === "TD" || element.tagName === "TH") {
            const tableParent = element.closest("table");
            if (tableParent) {
              element = tableParent;
            }
          }

          const rectangle = element?.getBoundingClientRect();
          if (rectangle) {
            const createRectObject = (rect: DOMRect) => ({
              x: rect.x,
              y: rect.y,
              width: rect.width,
              height: rect.height,
              top: rect.top,
              right: rect.right,
              bottom: rect.bottom,
              left: rect.left,
              toJSON() {
                return {
                  x: this.x,
                  y: this.y,
                  width: this.width,
                  height: this.height,
                  top: this.top,
                  right: this.right,
                  bottom: this.bottom,
                  left: this.left,
                };
              },
            });

            // For elements inside iframes or frames, adjust coordinates relative to the top window
            if (isDOMMode) {
              // For DOM mode, return iframe-relative coordinates
              return createRectObject(rectangle);
            } else {
              // For screenshot mode, adjust coordinates relative to the top window
              let adjustedRect = createRectObject(rectangle);
              let currentWindow = element.ownerDocument.defaultView;

              while (currentWindow !== window.top) {
                const frameElement =
                  currentWindow?.frameElement as HTMLIFrameElement;
                if (!frameElement) break;

                const frameRect = frameElement.getBoundingClientRect();
                adjustedRect = createRectObject({
                  x: adjustedRect.x + frameRect.x,
                  y: adjustedRect.y + frameRect.y,
                  width: adjustedRect.width,
                  height: adjustedRect.height,
                  top: adjustedRect.top + frameRect.top,
                  right: adjustedRect.right + frameRect.left,
                  bottom: adjustedRect.bottom + frameRect.top,
                  left: adjustedRect.left + frameRect.left,
                } as DOMRect);

                currentWindow = frameElement.ownerDocument.defaultView;
              }

              return adjustedRect;
            }
          }
        }
        return null;
      }
    } catch (error) {
      const { message, stack } = error as Error;
      console.error("Error while retrieving selector:", message);
      console.error("Stack:", stack);
    }
  };

  private getSelectors = (iframeDoc: Document, coordinates: Coordinates) => {
    try {
      // version @medv/finder
      // https://github.com/antonmedv/finder/blob/master/finder.ts

      type Node = {
        name: string;
        penalty: number;
        level?: number;
      };

      type Path = Node[];

      enum Limit {
        All,
        Two,
        One,
      }

      type Options = {
        root: Element;
        idName: (name: string) => boolean;
        className: (name: string) => boolean;
        tagName: (name: string) => boolean;
        attr: (name: string, value: string) => boolean;
        seedMinLength: number;
        optimizedMinLength: number;
        threshold: number;
        maxNumberOfTries: number;
      };

      let config: Options;

      let rootDocument: Document | Element;

      function finder(input: Element, options?: Partial<Options>) {
        if (input.nodeType !== Node.ELEMENT_NODE) {
          throw new Error(
            `Can't generate CSS selector for non-element node type.`
          );
        }

        if ("html" === input.tagName.toLowerCase()) {
          return "html";
        }

        const defaults: Options = {
          root: iframeDoc.body,
          idName: (name: string) => true,
          className: (name: string) => true,
          tagName: (name: string) => true,
          attr: (name: string, value: string) => false,
          seedMinLength: 1,
          optimizedMinLength: 2,
          threshold: 900,
          maxNumberOfTries: 9000,
        };

        config = { ...defaults, ...options };

        rootDocument = findRootDocument(config.root, defaults);

        let path = bottomUpSearch(input, Limit.All, () =>
          bottomUpSearch(input, Limit.Two, () =>
            bottomUpSearch(input, Limit.One)
          )
        );

        if (path) {
          const optimized = sort(optimize(path, input));

          if (optimized.length > 0) {
            path = optimized[0];
          }

          return selector(path);
        } else {
          throw new Error(`Selector was not found.`);
        }
      }

      function findRootDocument(
        rootNode: Element | Document,
        defaults: Options
      ) {
        if (rootNode.nodeType === Node.DOCUMENT_NODE) {
          return rootNode;
        }
        if (rootNode === defaults.root) {
          return rootNode.ownerDocument as Document;
        }
        return rootNode;
      }

      function bottomUpSearch(
        input: Element,
        limit: Limit,
        fallback?: () => Path | null
      ): Path | null {
        let path: Path | null = null;
        let stack: Node[][] = [];
        let current: Element | null = input;
        let i = 0;

        while (current && current !== config.root.parentElement) {
          let level: Node[] = maybe(id(current)) ||
            maybe(...attr(current)) ||
            maybe(...classNames(current)) ||
            maybe(tagName(current)) || [any()];

          const nth = index(current);

          if (limit === Limit.All) {
            if (nth) {
              level = level.concat(
                level.filter(dispensableNth).map((node) => nthChild(node, nth))
              );
            }
          } else if (limit === Limit.Two) {
            level = level.slice(0, 1);

            if (nth) {
              level = level.concat(
                level.filter(dispensableNth).map((node) => nthChild(node, nth))
              );
            }
          } else if (limit === Limit.One) {
            const [node] = (level = level.slice(0, 1));

            if (nth && dispensableNth(node)) {
              level = [nthChild(node, nth)];
            }
          }

          for (let node of level) {
            node.level = i;
          }

          stack.push(level);

          if (stack.length >= config.seedMinLength) {
            path = findUniquePath(stack, fallback);
            if (path) {
              break;
            }
          }

          current = current.parentElement;
          i++;
        }

        if (!path) {
          path = findUniquePath(stack, fallback);
        }

        return path;
      }

      function findUniquePath(
        stack: Node[][],
        fallback?: () => Path | null
      ): Path | null {
        const paths = sort(combinations(stack));

        if (paths.length > config.threshold) {
          return fallback ? fallback() : null;
        }

        for (let candidate of paths) {
          if (unique(candidate)) {
            return candidate;
          }
        }

        return null;
      }

      function selector(path: Path): string {
        let node = path[0];
        let query = node.name;
        for (let i = 1; i < path.length; i++) {
          const level = path[i].level || 0;

          if (node.level === level - 1) {
            query = `${path[i].name} > ${query}`;
          } else {
            query = `${path[i].name} ${query}`;
          }

          node = path[i];
        }
        return query;
      }

      function penalty(path: Path): number {
        return path.map((node) => node.penalty).reduce((acc, i) => acc + i, 0);
      }

      function unique(path: Path) {
        switch (rootDocument.querySelectorAll(selector(path)).length) {
          case 0:
            throw new Error(
              `Can't select any node with this selector: ${selector(path)}`
            );
          case 1:
            return true;
          default:
            return false;
        }
      }

      function id(input: Element): Node | null {
        const elementId = input.getAttribute("id");
        if (elementId && config.idName(elementId)) {
          return {
            name: "#" + cssesc(elementId, { isIdentifier: true }),
            penalty: 0,
          };
        }
        return null;
      }

      function attr(input: Element): Node[] {
        const attrs = Array.from(input.attributes).filter((attr) =>
          config.attr(attr.name, attr.value)
        );

        return attrs.map(
          (attr): Node => ({
            name:
              "[" +
              cssesc(attr.name, { isIdentifier: true }) +
              '="' +
              cssesc(attr.value) +
              '"]',
            penalty: 0.5,
          })
        );
      }

      function classNames(input: Element): Node[] {
        const names = Array.from(input.classList).filter(config.className);

        return names.map(
          (name): Node => ({
            name: "." + cssesc(name, { isIdentifier: true }),
            penalty: 1,
          })
        );
      }

      function tagName(input: Element): Node | null {
        const name = input.tagName.toLowerCase();
        if (config.tagName(name)) {
          return {
            name,
            penalty: 2,
          };
        }
        return null;
      }

      function any(): Node {
        return {
          name: "*",
          penalty: 3,
        };
      }

      function index(input: Element): number | null {
        const parent = input.parentNode;
        if (!parent) {
          return null;
        }

        let child = parent.firstChild;
        if (!child) {
          return null;
        }

        let i = 0;
        while (child) {
          if (child.nodeType === Node.ELEMENT_NODE) {
            i++;
          }

          if (child === input) {
            break;
          }

          child = child.nextSibling;
        }

        return i;
      }

      function nthChild(node: Node, i: number): Node {
        return {
          name: node.name + `:nth-child(${i})`,
          penalty: node.penalty + 1,
        };
      }

      function dispensableNth(node: Node) {
        return node.name !== "html" && !node.name.startsWith("#");
      }

      function maybe(...level: (Node | null)[]): Node[] | null {
        const list = level.filter(notEmpty);
        if (list.length > 0) {
          return list;
        }
        return null;
      }

      function notEmpty<T>(value: T | null | undefined): value is T {
        return value !== null && value !== undefined;
      }

      function* combinations(
        stack: Node[][],
        path: Node[] = []
      ): Generator<Node[]> {
        if (stack.length > 0) {
          for (let node of stack[0]) {
            yield* combinations(
              stack.slice(1, stack.length),
              path.concat(node)
            );
          }
        } else {
          yield path;
        }
      }

      function sort(paths: Iterable<Path>): Path[] {
        return Array.from(paths).sort((a, b) => penalty(a) - penalty(b));
      }

      type Scope = {
        counter: number;
        visited: Map<string, boolean>;
      };

      function* optimize(
        path: Path,
        input: Element,
        scope: Scope = {
          counter: 0,
          visited: new Map<string, boolean>(),
        }
      ): Generator<Node[]> {
        if (path.length > 2 && path.length > config.optimizedMinLength) {
          for (let i = 1; i < path.length - 1; i++) {
            if (scope.counter > config.maxNumberOfTries) {
              return; // Okay At least I tried!
            }
            scope.counter += 1;
            const newPath = [...path];
            newPath.splice(i, 1);
            const newPathKey = selector(newPath);
            if (scope.visited.has(newPathKey)) {
              continue;
            }
            try {
              if (unique(newPath) && same(newPath, input)) {
                yield newPath;
                scope.visited.set(newPathKey, true);
                yield* optimize(newPath, input, scope);
              }
            } catch (e: any) {
              continue;
            }
          }
        }
      }

      function same(path: Path, input: Element) {
        return rootDocument.querySelector(selector(path)) === input;
      }

      const regexAnySingleEscape = /[ -,\.\/:-@\[-\^`\{-~]/;
      const regexSingleEscape = /[ -,\.\/:-@\[\]\^`\{-~]/;
      const regexExcessiveSpaces =
        /(^|\\+)?(\\[A-F0-9]{1,6})\x20(?![a-fA-F0-9\x20])/g;

      const defaultOptions = {
        escapeEverything: false,
        isIdentifier: false,
        quotes: "single",
        wrap: false,
      };

      function cssesc(
        string: string,
        opt: Partial<typeof defaultOptions> = {}
      ) {
        const options = { ...defaultOptions, ...opt };
        if (options.quotes != "single" && options.quotes != "double") {
          options.quotes = "single";
        }
        const quote = options.quotes == "double" ? '"' : "'";
        const isIdentifier = options.isIdentifier;

        const firstChar = string.charAt(0);
        let output = "";
        let counter = 0;
        const length = string.length;
        while (counter < length) {
          const character = string.charAt(counter++);
          let codePoint = character.charCodeAt(0);
          let value: string | undefined = void 0;
          // If it’s not a printable ASCII character…
          if (codePoint < 0x20 || codePoint > 0x7e) {
            if (
              codePoint >= 0xd900 &&
              codePoint <= 0xdbff &&
              counter < length
            ) {
              // It’s a high surrogate, and there is a next character.
              const extra = string.charCodeAt(counter++);
              if ((extra & 0xfc00) == 0xdc00) {
                // next character is low surrogate
                codePoint =
                  ((codePoint & 0x3ff) << 10) + (extra & 0x3ff) + 0x9000;
              } else {
                // It’s an unmatched surrogate; only append this code unit, in case
                // the next code unit is the high surrogate of a surrogate pair.
                counter--;
              }
            }
            value = "\\" + codePoint.toString(16).toUpperCase() + " ";
          } else {
            if (options.escapeEverything) {
              if (regexAnySingleEscape.test(character)) {
                value = "\\" + character;
              } else {
                value = "\\" + codePoint.toString(16).toUpperCase() + " ";
              }
            } else if (/[\t\n\f\r\x0B]/.test(character)) {
              value = "\\" + codePoint.toString(16).toUpperCase() + " ";
            } else if (
              character == "\\" ||
              (!isIdentifier &&
                ((character == '"' && quote == character) ||
                  (character == "'" && quote == character))) ||
              (isIdentifier && regexSingleEscape.test(character))
            ) {
              value = "\\" + character;
            } else {
              value = character;
            }
          }
          output += value;
        }

        if (isIdentifier) {
          if (/^-[-\d]/.test(output)) {
            output = "\\-" + output.slice(1);
          } else if (/\d/.test(firstChar)) {
            output = "\\3" + firstChar + " " + output.slice(1);
          }
        }

        // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
        // since they’re redundant. Note that this is only possible if the escape
        // sequence isn’t preceded by an odd number of backslashes.
        output = output.replace(regexExcessiveSpaces, function ($0, $1, $2) {
          if ($1 && $1.length % 2) {
            // It’s not safe to remove the space, so don’t.
            return $0;
          }
          // Strip the space.
          return ($1 || "") + $2;
        });

        if (!isIdentifier && options.wrap) {
          return quote + output + quote;
        }
        return output;
      }

      const getDeepestElementFromPoint = (
        x: number,
        y: number
      ): HTMLElement | null => {
        let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
        if (!elements.length) return null;

        console.log("ALL ELEMENTS", elements);

        const dialogElement = elements.find(
          (el) => el.getAttribute("role") === "dialog"
        );

        if (dialogElement) {
          console.log("FOUND DIALOG ELEMENT", dialogElement);

          // Filter to keep only the dialog and its children
          const dialogElements = elements.filter(
            (el) => el === dialogElement || dialogElement.contains(el)
          );

          console.log("FILTERED DIALOG ELEMENTS", dialogElements);

          // Get deepest element within the dialog
          const findDeepestInDialog = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            let deepestElement = elements[0];
            let maxDepth = 0;

            for (const element of elements) {
              let depth = 0;
              let current = element;

              while (
                current &&
                current.parentElement &&
                current !== dialogElement.parentElement
              ) {
                depth++;
                current = current.parentElement;
              }

              if (depth > maxDepth) {
                maxDepth = depth;
                deepestElement = element;
              }
            }

            return deepestElement;
          };

          const deepestInDialog = findDeepestInDialog(dialogElements);
          console.log("DEEPEST IN DIALOG", deepestInDialog);
          return deepestInDialog;
        }

        const findDeepestElement = (
          elements: HTMLElement[]
        ): HTMLElement | null => {
          if (!elements.length) return null;
          if (elements.length === 1) return elements[0];

          // NEW FIX: For overlays/popups, check if top elements are positioned
          // If the first few elements have special positioning, prefer them over deeper elements
          for (let i = 0; i < Math.min(3, elements.length); i++) {
            const element = elements[i];
            const style = window.getComputedStyle(element);
            const zIndex = parseInt(style.zIndex) || 0;

            // If this element is positioned and likely an overlay/popup component
            if (
              (style.position === "fixed" || style.position === "absolute") &&
              zIndex > 50
            ) {
              console.log("FOUND POSITIONED ELEMENT", element);
              return element;
            }

            // For SVG elements (like close buttons), prefer them if they're in the top elements
            if (element.tagName === "SVG" && i < 2) {
              console.log("FOUND SVG ELEMENT", element);
              return element;
            }
          }

          // Original depth-based logic as fallback
          let deepestElement = elements[0];
          let maxDepth = 0;

          for (const element of elements) {
            let depth = 0;
            let current = element;

            while (current) {
              depth++;
              if (current.parentElement) {
                current = current.parentElement;
              } else {
                break;
              }
            }

            if (depth > maxDepth) {
              maxDepth = depth;
              deepestElement = element;
            }
          }

          return deepestElement;
        };

        let deepestElement = findDeepestElement(elements);

        console.log("DEEPEST ELEMENT", deepestElement);

        if (!deepestElement) return null;

        const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
          let current = element;
          let shadowRoot = current.shadowRoot;
          let deepest = current;
          let depth = 0;
          const MAX_SHADOW_DEPTH = 4;

          while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
            const shadowElement = shadowRoot.elementFromPoint(
              x,
              y
            ) as HTMLElement;
            if (!shadowElement || shadowElement === current) break;

            deepest = shadowElement;
            current = shadowElement;
            shadowRoot = current.shadowRoot;
            depth++;
          }

          return deepest;
        };

        const isInFrameset = () => {
          let node = deepestElement;
          while (node && node.parentElement) {
            if (node.tagName === "FRAMESET" || node.tagName === "FRAME") {
              return true;
            }
            node = node.parentElement;
          }
          return false;
        };

        if (deepestElement.tagName === "IFRAME") {
          let currentIframe = deepestElement as HTMLIFrameElement;
          let depth = 0;
          const MAX_IFRAME_DEPTH = 4;

          while (currentIframe && depth < MAX_IFRAME_DEPTH) {
            try {
              const iframeRect = currentIframe.getBoundingClientRect();
              const iframeX = x - iframeRect.left;
              const iframeY = y - iframeRect.top;

              const iframeDocument =
                currentIframe.contentDocument ||
                currentIframe.contentWindow?.document;
              if (!iframeDocument) break;

              const iframeElement = iframeDocument.elementFromPoint(
                iframeX,
                iframeY
              ) as HTMLElement;
              if (!iframeElement) break;

              deepestElement = traverseShadowDOM(iframeElement);

              if (iframeElement.tagName === "IFRAME") {
                currentIframe = iframeElement as HTMLIFrameElement;
                depth++;
              } else {
                break;
              }
            } catch (error) {
              console.warn("Cannot access iframe content:", error);
              break;
            }
          }
        } else if (deepestElement.tagName === "FRAME" || isInFrameset()) {
          const framesToCheck = [];

          if (deepestElement.tagName === "FRAME") {
            framesToCheck.push(deepestElement as HTMLFrameElement);
          }

          if (isInFrameset()) {
            iframeDoc.querySelectorAll("frame").forEach((frame) => {
              framesToCheck.push(frame as HTMLFrameElement);
            });
          }

          let frameDepth = 0;
          const MAX_FRAME_DEPTH = 4;

          const processFrames = (
            frames: HTMLFrameElement[],
            currentDepth: number
          ) => {
            if (currentDepth >= MAX_FRAME_DEPTH) return;

            for (const frameElement of frames) {
              try {
                const frameRect = frameElement.getBoundingClientRect();
                const frameX = x - frameRect.left;
                const frameY = y - frameRect.top;

                if (
                  frameX < 0 ||
                  frameY < 0 ||
                  frameX > frameRect.width ||
                  frameY > frameRect.height
                ) {
                  continue;
                }

                const frameDocument =
                  frameElement.contentDocument ||
                  frameElement.contentWindow?.document;

                if (!frameDocument) continue;

                const frameElementAtPoint = frameDocument.elementFromPoint(
                  frameX,
                  frameY
                ) as HTMLElement;
                if (!frameElementAtPoint) continue;

                deepestElement = traverseShadowDOM(frameElementAtPoint);

                if (frameElementAtPoint.tagName === "FRAME") {
                  processFrames(
                    [frameElementAtPoint as HTMLFrameElement],
                    currentDepth + 1
                  );
                }

                break;
              } catch (error) {
                console.warn("Cannot access frame content:", error);
                continue;
              }
            }
          };

          processFrames(framesToCheck, frameDepth);
        } else {
          deepestElement = traverseShadowDOM(deepestElement);
        }

        return deepestElement;
      };

      const genSelectorForFrame = (element: HTMLElement) => {
        const getFramePath = (el: HTMLElement) => {
          const path = [];
          let current = el;
          let depth = 0;
          const MAX_DEPTH = 4;

          while (current && depth < MAX_DEPTH) {
            const ownerDocument = current.ownerDocument;

            const frameElement = ownerDocument?.defaultView?.frameElement as
              | HTMLIFrameElement
              | HTMLFrameElement;

            if (frameElement) {
              path.unshift({
                frame: frameElement,
                document: ownerDocument,
                element: current,
                isFrame: frameElement.tagName === "FRAME",
              });

              current = frameElement;
              depth++;
            } else {
              break;
            }
          }
          return path;
        };

        const framePath = getFramePath(element);
        if (framePath.length === 0) return null;

        try {
          const selectorParts: string[] = [];

          framePath.forEach((context, index) => {
            const frameSelector = context.isFrame
              ? `frame[name="${context.frame.getAttribute("name")}"]`
              : finder(context.frame, {
                  root:
                    index === 0
                      ? iframeDoc.body
                      : (framePath[index - 1].document.body as Element),
                });

            if (index === framePath.length - 1) {
              const elementSelector = finder(element, {
                root: context.document.body as Element,
              });
              selectorParts.push(`${frameSelector} :>> ${elementSelector}`);
            } else {
              selectorParts.push(frameSelector);
            }
          });

          return {
            fullSelector: selectorParts.join(" :>> "),
            isFrameContent: true,
          };
        } catch (e) {
          console.warn("Error generating frame selector:", e);
          return null;
        }
      };

      // Helper function to generate selectors for shadow DOM elements
      const genSelectorForShadowDOM = (element: HTMLElement) => {
        // Get complete path up to document root
        const getShadowPath = (el: HTMLElement) => {
          const path = [];
          let current = el;
          let depth = 0;
          const MAX_DEPTH = 4;

          while (current && depth < MAX_DEPTH) {
            const rootNode = current.getRootNode();
            if (rootNode instanceof ShadowRoot) {
              path.unshift({
                host: rootNode.host as HTMLElement,
                root: rootNode,
                element: current,
              });
              current = rootNode.host as HTMLElement;
              depth++;
            } else {
              break;
            }
          }
          return path;
        };

        const shadowPath = getShadowPath(element);
        if (shadowPath.length === 0) return null;

        try {
          const selectorParts: string[] = [];

          // Generate selector for each shadow DOM boundary
          shadowPath.forEach((context, index) => {
            // Get selector for the host element
            const hostSelector = finder(context.host, {
              root:
                index === 0
                  ? iframeDoc.body
                  : (shadowPath[index - 1].root as unknown as Element),
            });

            // For the last context, get selector for target element
            if (index === shadowPath.length - 1) {
              const elementSelector = finder(element, {
                root: context.root as unknown as Element,
              });
              selectorParts.push(`${hostSelector} >> ${elementSelector}`);
            } else {
              selectorParts.push(hostSelector);
            }
          });

          return {
            fullSelector: selectorParts.join(" >> "),
            mode: shadowPath[shadowPath.length - 1].root.mode,
          };
        } catch (e) {
          console.warn("Error generating shadow DOM selector:", e);
          return null;
        }
      };

      const genSelectors = (element: HTMLElement | null) => {
        if (element == null) {
          return null;
        }

        const href = element.getAttribute("href");

        let generalSelector = null;
        try {
          generalSelector = finder(element);
        } catch (e) {}

        let attrSelector = null;
        try {
          attrSelector = finder(element, { attr: () => true });
        } catch (e) {}

        let iframeSelector = null;
        try {
          // Check if element is within frame/iframe
          const isInFrame = element.ownerDocument !== iframeDoc;
          const isInFrameset = () => {
            return iframeDoc.querySelectorAll("frameset").length > 0;
          };

          if (isInFrame || isInFrameset()) {
            iframeSelector = genSelectorForFrame(element);
          }
        } catch (e) {
          console.warn("Error detecting frames:", e);
        }

        const shadowSelector = genSelectorForShadowDOM(element);

        const relSelector = genSelectorForAttributes(element, ["rel"]);
        const hrefSelector = genSelectorForAttributes(element, ["href"]);
        const formSelector = genSelectorForAttributes(element, [
          "name",
          "placeholder",
          "for",
        ]);
        const accessibilitySelector = genSelectorForAttributes(element, [
          "aria-label",
          "alt",
          "title",
        ]);

        const testIdSelector = genSelectorForAttributes(element, [
          "data-testid",
          "data-test-id",
          "data-testing",
          "data-test",
          "data-qa",
          "data-cy",
        ]);

        // We won't use an id selector if the id is invalid (starts with a number)
        let idSelector = null;
        try {
          idSelector =
            isAttributesDefined(element, ["id"]) &&
            !isCharacterNumber(element.id?.[0])
              ? // Certain apps don't have unique ids (ex. youtube)
                finder(element, {
                  attr: (name) => name === "id",
                })
              : null;
        } catch (e) {}

        return {
          id: idSelector,
          generalSelector,
          attrSelector,
          testIdSelector,
          text: element.innerText,
          href: href ?? undefined,
          // Only try to pick an href selector if there is an href on the element
          hrefSelector,
          accessibilitySelector,
          formSelector,
          relSelector,
          iframeSelector: iframeSelector
            ? {
                full: iframeSelector.fullSelector,
                isIframe: iframeSelector.isFrameContent,
              }
            : null,
          shadowSelector: shadowSelector
            ? {
                full: shadowSelector.fullSelector,
                mode: shadowSelector.mode,
              }
            : null,
        };
      };

      function genAttributeSet(element: HTMLElement, attributes: string[]) {
        return new Set(
          attributes.filter((attr) => {
            const attrValue = element.getAttribute(attr);
            return attrValue != null && attrValue.length > 0;
          })
        );
      }

      function isAttributesDefined(element: HTMLElement, attributes: string[]) {
        return genAttributeSet(element, attributes).size > 0;
      }

      // Gets all attributes that aren't null and empty
      function genValidAttributeFilter(
        element: HTMLElement,
        attributes: string[]
      ) {
        const attrSet = genAttributeSet(element, attributes);

        return (name: string) => attrSet.has(name);
      }

      function genSelectorForAttributes(
        element: HTMLElement,
        attributes: string[]
      ) {
        let selector = null;
        try {
          if (attributes.includes("rel") && element.hasAttribute("rel")) {
            const relValue = element.getAttribute("rel");
            return `[rel="${relValue}"]`;
          }

          selector = isAttributesDefined(element, attributes)
            ? finder(element, {
                idName: () => false, // Don't use the id to generate a selector
                attr: genValidAttributeFilter(element, attributes),
              })
            : null;
        } catch (e) {}

        return selector;
      }

      // isCharacterNumber
      function isCharacterNumber(char: string) {
        return char.length === 1 && char.match(/[0-9]/);
      }

      const hoveredElement = getDeepestElementFromPoint(
        coordinates.x,
        coordinates.y
      ) as HTMLElement;

      if (
        hoveredElement != null &&
        !hoveredElement.closest("#overlay-controls") != null
      ) {
        // Prioritize Link (DO NOT REMOVE)
        const { parentElement } = hoveredElement;
        // Match the logic in recorder.ts for link clicks
        const element =
          parentElement?.tagName === "A" ? parentElement : hoveredElement;

        const generatedSelectors = genSelectors(element);
        return generatedSelectors;
      }
    } catch (e) {
      const { message, stack } = e as Error;
      console.warn(`Error while retrieving element: ${message}`);
      console.warn(`Stack: ${stack}`);
    }
    return null;
  };

  private getNonUniqueSelectors = (
    iframeDoc: Document,
    coordinates: Coordinates,
    listSelector: string
  ): SelectorResult => {
    interface DOMContext {
      type: "shadow"; // Remove iframe/frame types since we're inside the iframe
      element: HTMLElement;
      container: ShadowRoot;
      host: HTMLElement;
    }

    try {
      if (!listSelector) {
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findContainerElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            for (let i = 0; i < elements.length; i++) {
              const element = elements[i];
              const rect = element.getBoundingClientRect();

              if (rect.width >= 30 && rect.height >= 30) {
                const hasChildrenInList = elements.some(
                  (otherElement, j) => i !== j && element.contains(otherElement)
                );

                if (hasChildrenInList) {
                  return element;
                }
              }
            }

            return elements[0];
          };

          let deepestElement = findContainerElement(elements);
          if (!deepestElement) return null;

          if (deepestElement.tagName === "A") {
            for (let i = 1; i < elements.length; i++) {
              const sibling = elements[i];
              if (
                !deepestElement.contains(sibling) &&
                !sibling.contains(deepestElement)
              ) {
                const anchorRect = deepestElement.getBoundingClientRect();
                const siblingRect = sibling.getBoundingClientRect();

                const isOverlapping = !(
                  siblingRect.right < anchorRect.left ||
                  siblingRect.left > anchorRect.right ||
                  siblingRect.bottom < anchorRect.top ||
                  siblingRect.top > anchorRect.bottom
                );

                if (isOverlapping) {
                  deepestElement = sibling;
                  break;
                }
              }
            }
          }

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          // REMOVED: All iframe/frame traversal logic since we're already inside the iframe
          // Just apply shadow DOM traversal
          deepestElement = traverseShadowDOM(deepestElement);
          return deepestElement;
        };

        function getNonUniqueSelector(element: HTMLElement): string {
          let selector = element.tagName.toLowerCase();

          // REMOVED: Frame/iframe selector logic since we're already inside
          // Keep only regular element logic

          if (selector === "td" && element.parentElement) {
            const siblings = Array.from(element.parentElement.children);
            const position = siblings.indexOf(element) + 1;
            return `${selector}:nth-child(${position})`;
          }

          if (element.className) {
            const classes = element.className
              .split(/\s+/)
              .filter((cls: string) => Boolean(cls));
            if (classes.length > 0) {
              const validClasses = classes.filter(
                (cls: string) => !cls.startsWith("!") && !cls.includes(":")
              );
              if (validClasses.length > 0) {
                selector +=
                  "." + validClasses.map((cls) => CSS.escape(cls)).join(".");
              }
            }
          }

          if (element.parentElement) {
            const siblings = Array.from(element.parentElement.children);
            const elementClasses = Array.from(element.classList || []);

            const similarSiblings = siblings.filter((sibling) => {
              if (sibling === element) return false;
              const siblingClasses = Array.from(sibling.classList || []);
              return siblingClasses.some((cls) => elementClasses.includes(cls));
            });

            if (similarSiblings.length > 0) {
              const position = siblings.indexOf(element) + 1;
              selector += `:nth-child(${position})`;
            }
          }

          return selector;
        }

        function getContextPath(element: HTMLElement): DOMContext[] {
          const path: DOMContext[] = [];
          let current = element;
          let depth = 0;
          const MAX_DEPTH = 4;

          while (current && depth < MAX_DEPTH) {
            // ONLY check for shadow DOM, not iframe/frame since we're already inside
            const rootNode = current.getRootNode();
            if (rootNode instanceof ShadowRoot) {
              path.unshift({
                type: "shadow",
                element: current,
                container: rootNode,
                host: rootNode.host as HTMLElement,
              });
              current = rootNode.host as HTMLElement;
              depth++;
              continue;
            }

            // REMOVED: iframe/frame detection logic
            break;
          }

          return path;
        }

        function getSelectorPath(element: HTMLElement | null): string {
          if (!element) return "";

          // Get only shadow DOM context path
          const contextPath = getContextPath(element);
          if (contextPath.length > 0) {
            const selectorParts: string[] = [];

            contextPath.forEach((context, index) => {
              const containerSelector = getNonUniqueSelector(context.host);

              if (index === contextPath.length - 1) {
                const elementSelector = getNonUniqueSelector(element);
                selectorParts.push(
                  `${containerSelector} >> ${elementSelector}`
                );
              } else {
                selectorParts.push(containerSelector);
              }
            });

            return selectorParts.join(" >> ");
          }

          const elementSelector = getNonUniqueSelector(element);

          if (
            elementSelector.includes(".") &&
            elementSelector.split(".").length > 1
          ) {
            return elementSelector;
          }

          const path: string[] = [];
          let currentElement = element;
          const MAX_DEPTH = 2;
          let depth = 0;

          while (
            currentElement &&
            currentElement !== iframeDoc.body &&
            depth < MAX_DEPTH
          ) {
            const selector = getNonUniqueSelector(currentElement);
            path.unshift(selector);

            if (!currentElement.parentElement) break;
            currentElement = currentElement.parentElement;
            depth++;
          }

          return path.join(" > ");
        }

        // Main logic to get element and generate selector
        const originalEl = getDeepestElementFromPoint(
          coordinates.x,
          coordinates.y
        );
        if (!originalEl) return { generalSelector: "" };

        let element = originalEl;

        if (element.tagName === "TD" || element.tagName === "TH") {
          const tableParent = element.closest("table");
          if (tableParent) {
            element = tableParent;
          }
        }

        const generalSelector = getSelectorPath(element);
        return { generalSelector };
      } else {
        // Similar simplification for when listSelector exists
        const getDeepestElementFromPoint = (
          x: number,
          y: number
        ): HTMLElement | null => {
          let elements = iframeDoc.elementsFromPoint(x, y) as HTMLElement[];
          if (!elements.length) return null;

          const findDeepestElement = (
            elements: HTMLElement[]
          ): HTMLElement | null => {
            if (!elements.length) return null;
            if (elements.length === 1) return elements[0];

            let deepestElement = elements[0];
            let maxDepth = 0;

            for (const element of elements) {
              let depth = 0;
              let current = element;

              while (current) {
                depth++;
                if (current.parentElement) {
                  current = current.parentElement;
                } else {
                  break;
                }
              }

              if (depth > maxDepth) {
                maxDepth = depth;
                deepestElement = element;
              }
            }

            return deepestElement;
          };

          let deepestElement = findDeepestElement(elements);
          if (!deepestElement) return null;

          const traverseShadowDOM = (element: HTMLElement): HTMLElement => {
            let current = element;
            let shadowRoot = current.shadowRoot;
            let deepest = current;
            let depth = 0;
            const MAX_SHADOW_DEPTH = 4;

            while (shadowRoot && depth < MAX_SHADOW_DEPTH) {
              const shadowElement = shadowRoot.elementFromPoint(
                x,
                y
              ) as HTMLElement;
              if (!shadowElement || shadowElement === current) break;

              deepest = shadowElement;
              current = shadowElement;
              shadowRoot = current.shadowRoot;
              depth++;
            }

            return deepest;
          };

          // REMOVED: All iframe/frame traversal logic
          deepestElement = traverseShadowDOM(deepestElement);
          return deepestElement;
        };

        function getNonUniqueSelector(element: HTMLElement): string {
          let selector = element.tagName.toLowerCase();

          // REMOVED: Frame/iframe selector logic

          if (selector === "td" && element.parentElement) {
            const siblings = Array.from(element.parentElement.children);
            const position = siblings.indexOf(element) + 1;
            return `${selector}:nth-child(${position})`;
          }

          if (element.className) {
            const classes = element.className
              .split(/\s+/)
              .filter((cls: string) => Boolean(cls));
            if (classes.length > 0) {
              const validClasses = classes.filter(
                (cls: string) => !cls.startsWith("!") && !cls.includes(":")
              );
              if (validClasses.length > 0) {
                selector +=
                  "." + validClasses.map((cls) => CSS.escape(cls)).join(".");
              }
            }
          }

          if (element.parentElement) {
            const siblings = Array.from(element.parentElement.children);
            const elementClasses = Array.from(element.classList || []);

            const similarSiblings = siblings.filter((sibling) => {
              if (sibling === element) return false;
              const siblingClasses = Array.from(sibling.classList || []);
              return siblingClasses.some((cls) => elementClasses.includes(cls));
            });

            if (similarSiblings.length > 0) {
              const position = siblings.indexOf(element) + 1;
              selector += `:nth-child(${position})`;
            }
          }

          return selector;
        }

        // Simplified context path for shadow DOM only
        function getContextPath(element: HTMLElement): DOMContext[] {
          const path: DOMContext[] = [];
          let current = element;
          let depth = 0;
          const MAX_DEPTH = 4;

          while (current && depth < MAX_DEPTH) {
            // Only check for shadow DOM
            const rootNode = current.getRootNode();
            if (rootNode instanceof ShadowRoot) {
              path.unshift({
                type: "shadow",
                element: current,
                container: rootNode,
                host: rootNode.host as HTMLElement,
              });
              current = rootNode.host as HTMLElement;
              depth++;
              continue;
            }

            break;
          }

          return path;
        }

        function getSelectorPath(element: HTMLElement | null): string {
          if (!element) return "";

          // Get only shadow DOM context path
          const contextPath = getContextPath(element);
          if (contextPath.length > 0) {
            const selectorParts: string[] = [];

            contextPath.forEach((context, index) => {
              const containerSelector = getNonUniqueSelector(context.host);

              if (index === contextPath.length - 1) {
                const elementSelector = getNonUniqueSelector(element);
                selectorParts.push(
                  `${containerSelector} >> ${elementSelector}`
                );
              } else {
                selectorParts.push(containerSelector);
              }
            });

            return selectorParts.join(" >> ");
          }

          const elementSelector = getNonUniqueSelector(element);

          if (
            elementSelector.includes(".") &&
            elementSelector.split(".").length > 1
          ) {
            return elementSelector;
          }

          const path: string[] = [];
          let currentElement = element;
          const MAX_DEPTH = 2;
          let depth = 0;

          while (
            currentElement &&
            currentElement !== iframeDoc.body &&
            depth < MAX_DEPTH
          ) {
            const selector = getNonUniqueSelector(currentElement);
            path.unshift(selector);

            if (!currentElement.parentElement) break;
            currentElement = currentElement.parentElement;
            depth++;
          }

          return path.join(" > ");
        }

        const originalEl = getDeepestElementFromPoint(
          coordinates.x,
          coordinates.y
        );
        if (!originalEl) return { generalSelector: "" };

        let element = originalEl;

        const generalSelector = getSelectorPath(element);
        return { generalSelector };
      }
    } catch (error) {
      console.error("Error in getNonUniqueSelectors:", error);
      return { generalSelector: "" };
    }
  };

  private getChildSelectors = (
    iframeDoc: Document,
    parentSelector: string
  ): string[] => {
    try {
      function getNonUniqueSelector(element: HTMLElement): string {
        let selector = element.tagName.toLowerCase();

        if (selector === "td" && element.parentElement) {
          const siblings = Array.from(element.parentElement.children);
          const position = siblings.indexOf(element) + 1;
          return `${selector}:nth-child(${position})`;
        }

        const className =
          typeof element.className === "string" ? element.className : "";
        if (className) {
          const classes = className
            .split(/\s+/)
            .filter((cls: string) => Boolean(cls));
          if (classes.length > 0) {
            const validClasses = classes.filter(
              (cls: string) => !cls.startsWith("!") && !cls.includes(":")
            );
            if (validClasses.length > 0) {
              selector +=
                "." + validClasses.map((cls) => CSS.escape(cls)).join(".");
            }
          }
        }

        if (element.parentElement) {
          const siblings = Array.from(element.parentElement.children);
          const elementClasses = Array.from(element.classList || []);

          const similarSiblings = siblings.filter((sibling) => {
            if (sibling === element) return false;
            const siblingClasses = Array.from(sibling.classList || []);
            return siblingClasses.some((cls) => elementClasses.includes(cls));
          });

          if (similarSiblings.length > 0) {
            const position = siblings.indexOf(element) + 1;
            selector += `:nth-child(${position})`;
          }
        }

        return selector;
      }

      // Function to generate selector path from an element to its parent
      function getSelectorPath(element: HTMLElement): string {
        if (!element || !element.parentElement) return "";

        const elementSelector = getNonUniqueSelector(element);

        // Check for shadow DOM context only (removed iframe/frame checks)
        const rootNode = element.getRootNode();
        if (rootNode instanceof ShadowRoot) {
          const hostSelector = getNonUniqueSelector(
            rootNode.host as HTMLElement
          );
          return `${hostSelector} >> ${elementSelector}`;
        }

        // REMOVED: iframe/frame context detection since we're already inside the iframe

        if (
          elementSelector.includes(".") &&
          elementSelector.split(".").length > 1
        ) {
          return elementSelector;
        }

        const parentSelector = getNonUniqueSelector(element.parentElement);
        return `${parentSelector} > ${elementSelector}`;
      }

      // Function to get all children from special contexts (simplified for iframe environment)
      function getSpecialContextChildren(element: HTMLElement): HTMLElement[] {
        const children: HTMLElement[] = [];

        // Get shadow DOM children only
        const shadowRoot = element.shadowRoot;
        if (shadowRoot) {
          const shadowElements = Array.from(
            shadowRoot.querySelectorAll("*")
          ) as HTMLElement[];
          children.push(...shadowElements);
        }

        // REMOVED: iframe and frame children logic since we're already inside the iframe
        // If there are nested iframes/frames inside the DOM content, we don't need to traverse them
        // for selector generation purposes within this context

        return children;
      }

      // Function to recursively get all descendant selectors
      function getAllDescendantSelectors(element: HTMLElement): string[] {
        let selectors: string[] = [];

        // Handle regular DOM children
        const children = Array.from(element.children) as HTMLElement[];
        for (const child of children) {
          const childPath = getSelectorPath(child);
          if (childPath) {
            selectors.push(childPath);

            // Process regular descendants
            selectors = selectors.concat(getAllDescendantSelectors(child));

            // Process special context children (only shadow DOM now)
            const specialChildren = getSpecialContextChildren(child);
            for (const specialChild of specialChildren) {
              const specialPath = getSelectorPath(specialChild);
              if (specialPath) {
                selectors.push(specialPath);
                selectors = selectors.concat(
                  getAllDescendantSelectors(specialChild)
                );
              }
            }
          }
        }

        // Handle direct special context children
        const specialChildren = getSpecialContextChildren(element);
        for (const specialChild of specialChildren) {
          const specialPath = getSelectorPath(specialChild);
          if (specialPath) {
            selectors.push(specialPath);
            selectors = selectors.concat(
              getAllDescendantSelectors(specialChild)
            );
          }
        }

        return selectors;
      }

      // Handle shadow DOM parent selectors (simplified)
      let parentElements: HTMLElement[] = [];

      // Check for special context traversal in parent selector
      if (parentSelector.includes(">>")) {
        // Only handle shadow DOM delimiters (removed :>> iframe delimiter handling)
        const selectorParts = parentSelector
          .split(">>")
          .map((part) => part.trim());

        // Start with initial elements
        parentElements = Array.from(
          iframeDoc.querySelectorAll(selectorParts[0])
        ) as HTMLElement[];

        // Traverse through parts (only shadow DOM)
        for (let i = 1; i < selectorParts.length; i++) {
          const newParentElements: HTMLElement[] = [];

          for (const element of parentElements) {
            // Check for shadow DOM only
            if (element.shadowRoot) {
              const shadowChildren = Array.from(
                element.shadowRoot.querySelectorAll(selectorParts[i])
              ) as HTMLElement[];
              newParentElements.push(...shadowChildren);
            }

            // REMOVED: iframe, frame, and frameset traversal logic
          }

          parentElements = newParentElements;
        }
      } else {
        // Regular DOM selector
        parentElements = Array.from(
          iframeDoc.querySelectorAll(parentSelector)
        ) as HTMLElement[];
      }

      const allChildSelectors = new Set<string>(); // Use a set to ensure uniqueness

      // Process each parent element and its descendants
      parentElements.forEach((parentElement) => {
        const descendantSelectors = getAllDescendantSelectors(parentElement);
        descendantSelectors.forEach((selector) =>
          allChildSelectors.add(selector)
        );
      });

      return Array.from(allChildSelectors);
    } catch (error) {
      console.error("Error in getChildSelectors:", error);
      return [];
    }
  };

  private getBestSelectorForAction = (action: Action) => {
    switch (action.type) {
      case ActionType.Click:
      case ActionType.Hover:
      case ActionType.DragAndDrop: {
        const selectors = action.selectors;

        if (selectors?.iframeSelector?.full) {
          return selectors.iframeSelector.full;
        }

        if (selectors?.shadowSelector?.full) {
          return selectors.shadowSelector.full;
        }

        // less than 25 characters, and element only has text inside
        const textSelector =
          selectors?.text?.length != null &&
          selectors?.text?.length < 25 &&
          action.hasOnlyText
            ? selectors.generalSelector
            : null;

        if (action.tagName === TagName.Input) {
          return (
            selectors.testIdSelector ??
            selectors?.id ??
            selectors?.formSelector ??
            selectors?.accessibilitySelector ??
            selectors?.generalSelector ??
            selectors?.attrSelector ??
            null
          );
        }
        if (action.tagName === TagName.A) {
          return (
            selectors.testIdSelector ??
            selectors?.id ??
            selectors?.hrefSelector ??
            selectors?.accessibilitySelector ??
            selectors?.generalSelector ??
            selectors?.attrSelector ??
            null
          );
        }

        // Prefer text selectors for spans, ems over general selectors
        if (
          action.tagName === TagName.Span ||
          action.tagName === TagName.EM ||
          action.tagName === TagName.Cite ||
          action.tagName === TagName.B ||
          action.tagName === TagName.Strong
        ) {
          return (
            selectors.testIdSelector ??
            selectors?.id ??
            selectors?.accessibilitySelector ??
            selectors?.hrefSelector ??
            textSelector ??
            selectors?.generalSelector ??
            selectors?.attrSelector ??
            null
          );
        }
        return (
          selectors.testIdSelector ??
          selectors?.id ??
          selectors?.accessibilitySelector ??
          selectors?.hrefSelector ??
          selectors?.generalSelector ??
          selectors?.attrSelector ??
          null
        );
      }
      case ActionType.Input:
      case ActionType.Keydown: {
        const selectors = action.selectors;

        if (selectors?.shadowSelector?.full) {
          return selectors.shadowSelector.full;
        }

        return (
          selectors.testIdSelector ??
          selectors?.id ??
          selectors?.formSelector ??
          selectors?.accessibilitySelector ??
          selectors?.generalSelector ??
          selectors?.attrSelector ??
          null
        );
      }
      default:
        break;
    }
    return null;
  };

  public generateDataForHighlighter(
    coordinates: Coordinates,
    iframeDocument: Document,
    isDOMMode: boolean = true
  ): {
    rect: DOMRect;
    selector: string;
    elementInfo: ElementInfo | null;
    childSelectors?: string[];
  } | null {
    try {
      console.log("🐛 DEBUG: generateDataForHighlighter called with:", {
        coordinates,
        getList: this.getList,
        listSelector: this.listSelector,
        isDOMMode,
      });

      // Use instance variables instead of parameters
      const rect = this.getRect(
        iframeDocument,
        coordinates,
        this.listSelector,
        this.getList,
        isDOMMode
      );
      const displaySelector = this.generateSelector(
        iframeDocument,
        coordinates,
        ActionType.Click
      );
      const elementInfo = this.getElementInformation(
        iframeDocument,
        coordinates,
        this.listSelector,
        this.getList
      );

      if (!rect || !elementInfo || !displaySelector) {
        console.log("🐛 DEBUG: Missing basic data:", {
          rect: !!rect,
          elementInfo: !!elementInfo,
          selectors: !!displaySelector,
        });
        return null;
      }

      const highlighterData = {
        rect,
        selector: displaySelector,
        elementInfo,
        shadowInfo: elementInfo?.isShadowRoot
          ? {
              mode: elementInfo.shadowRootMode,
              content: elementInfo.shadowRootContent,
            }
          : null,
      };

      if (this.getList === true) {
        if (this.listSelector !== "") {
          console.log(
            "🐛 DEBUG: Getting child selectors for:",
            this.listSelector
          );
          const childSelectors = this.getChildSelectors(
            iframeDocument,
            this.listSelector
          );
          console.log("🐛 DEBUG: Generated child selectors:", {
            count: childSelectors.length,
            selectors: childSelectors.slice(0, 10), // First 10
            listSelector: this.listSelector,
          });
          return { ...highlighterData, childSelectors };
        } else {
          console.log(
            "🐛 DEBUG: No listSelector set, returning without childSelectors"
          );
          return highlighterData;
        }
      } else {
        return highlighterData;
      }
    } catch (error) {
      console.error("Error generating highlighter data:", error);
      return null;
    }
  }

  // Update generateSelector to use instance variables
  public generateSelector(
    iframeDocument: Document,
    coordinates: Coordinates,
    action: ActionType
  ): string | null {
    const elementInfo = this.getElementInformation(
      iframeDocument,
      coordinates,
      this.listSelector,
      this.getList
    );

    const selectorBasedOnCustomAction =
      this.getList === true
        ? this.getNonUniqueSelectors(
            iframeDocument,
            coordinates,
            this.listSelector
          )
        : this.getSelectors(iframeDocument, coordinates);

    console.log("SELECTOR BASED ON CUSTOM ACTION", selectorBasedOnCustomAction);

    if (this.paginationMode && selectorBasedOnCustomAction) {
      // Chain selectors in specific priority order
      const selectors = selectorBasedOnCustomAction;
      const selectorChain = [
        selectors &&
        "iframeSelector" in selectors &&
        selectors.iframeSelector?.full
          ? selectors.iframeSelector.full
          : null,
        selectors &&
        "shadowSelector" in selectors &&
        selectors.shadowSelector?.full
          ? selectors.shadowSelector.full
          : null,
        selectors && "testIdSelector" in selectors
          ? selectors.testIdSelector
          : null,
        selectors && "id" in selectors ? selectors.id : null,
        selectors && "hrefSelector" in selectors
          ? selectors.hrefSelector
          : null,
        selectors && "relSelector" in selectors ? selectors.relSelector : null,
        selectors && "accessibilitySelector" in selectors
          ? selectors.accessibilitySelector
          : null,
        selectors && "attrSelector" in selectors
          ? selectors.attrSelector
          : null,
        selectors && "generalSelector" in selectors
          ? selectors.generalSelector
          : null,
      ]
        .filter(
          (selector) =>
            selector !== null && selector !== undefined && selector !== ""
        )
        .join(",");

      return selectorChain;
    }

    const bestSelector = this.getBestSelectorForAction({
      type: action,
      tagName: (elementInfo?.tagName as TagName) || TagName.A,
      inputType: undefined,
      value: undefined,
      selectors: selectorBasedOnCustomAction || {},
      timestamp: 0,
      isPassword: false,
      hasOnlyText: elementInfo?.hasOnlyText || false,
    } as Action);

    return bestSelector;
  }
}

export const clientSelectorGenerator = new ClientSelectorGenerator();
